import hashlib
import json
import os
import shutil
import uuid

from utils.logger import debug,error,info
from utils.compiler import Compiler
from utils.language import c_lang_config, cpp_lang_config, java_lang_config, c_lang_spj_config, \
    c_lang_spj_compile, py2_lang_config, py3_lang_config, go_lang_config


from utils.server import JudgeClient
from utils.db import engine


_max_times = 1

JUDGE_FILE = '/home/charlie/Projects/my/judger_file'
JUDGE_RUN_BASE = JUDGE_FILE + '/run'
TEST_CASE_DIR = JUDGE_FILE + '/data'
DEBUG = True


class InitSubmissionEnv(object):
    def __init__(self, judger_workspace, submission_id, init_test_case_dir=False):
        self.work_dir = os.path.join(JUDGE_RUN_BASE, submission_id)
        self.init_test_case_dir = init_test_case_dir
        if init_test_case_dir:
            self.test_case_dir = os.path.join(self.work_dir, "submission_" + submission_id)
        else:
            self.test_case_dir = None
    

    def __enter__(self):
        try:
            os.mkdir(self.work_dir)
            if self.init_test_case_dir:
                os.mkdir(self.test_case_dir)
        except Exception as e:
            logger.exception(e)
            raise JudgeClientError("failed to create runtime dir")
        return self.work_dir, self.test_case_dir

    def __exit__(self, exc_type, exc_val, exc_tb):
        if not DEBUG:
            try:
                shutil.rmtree(self.work_dir)
            except Exception as e:
                logger.exception(e)
                raise JudgeClientError("failed to clean runtime dir")

class JudgeHandle():

    @classmethod
    def handle(cls,message, cur_time = _max_times):
        #如果超过最大次数直接舍弃
        if cur_time <= 0: 

            return

        try:
            body = message.body
            data = json.loads(body)
            cls._handle(data)
        except Exception as e:
            error(e)
            cls.handle(message, cur_time-1)
    @classmethod
    def _handle(cls,data):
        # 1.解析出所有的参数：语言，源代码，cpu_内存,测试集目录or测试用例，是否需要测试结果
        # 2.初始化环境
        # 3.编译
        # 4.调用run
        # 5.写入数据库
        lang = data['lang']
        src = data['src']
        max_cpu_time = data['max_cpu_time']
        max_memory = data['max_memory']
        test_case = data['test_case']
        output = data['output']
        judge_state_id = data['judge_state_id']
        # debug(lang, src, max_cpu_time, max_memory, test_case, output)
        
        language_config = c_lang_config
        if lang == 'cpp':
            language_config = cpp_lang_config
        elif lang == 'java':
            language_config = java_lang_config
        elif language == 'py2':
            language_config = py2_lang_config
        elif language == 'py3':
            language_config = py3_lang_config

        compile_config = language_config.get("compile")
        run_config = language_config["run"]

        # 2:创建工作目录（）和测试集目录
        has_test = bool(test_case)
        sub_id = uuid.uuid4().hex
        with InitSubmissionEnv(JUDGE_RUN_BASE, submission_id=str(sub_id), init_test_case_dir=has_test) as dirs:
                submission_dir, test_case_dir = dirs
                test_case_dir = test_case_dir or os.path.join(TEST_CASE_DIR, test_case_id)

                # 3.编译
                if compile_config:
                    src_path = os.path.join(submission_dir, compile_config["src_name"])

                    with open(src_path, "w", encoding="utf-8") as f:
                        f.write(src)
                    os.chmod(src_path, 0o444)

                    exe_path = Compiler().compile(compile_config=compile_config,
                                                src_path=src_path,
                                                output_dir=submission_dir)
                    os.chmod(exe_path, 0o555)
                else:
                    exe_path = os.path.join(submission_dir, run_config["exe_name"])
                    with open(exe_path, "w", encoding="utf-8") as f:
                        f.write(src)
                # 如果有参数中有测试集，需要将测试集写入文件
                # test_case: [{"input":"xxx","output":"xxx"},{}]
                if has_test:
                    info = {"test_case_number": len(test_case), "spj": False, "test_cases": {}}
                    
                    for index, item in enumerate(test_case):
                        index += 1
                        item_info = {}

                        input_name = str(index) + ".in"
                        item_info["input_name"] = input_name
                        input_data = item["input"].encode("utf-8")
                        item_info["input_size"] = len(input_data)

                        with open(os.path.join(test_case_dir, input_name), "wb") as f:
                            f.write(input_data)

                        info["test_cases"][index] = item_info

                    with open(os.path.join(test_case_dir, "info"), "w") as f:
                        json.dump(info, f)
                print('运行')
                # 运行
                judge_client = JudgeClient(run_config=language_config["run"],
                                        exe_path=exe_path,
                                        max_cpu_time=max_cpu_time,
                                        max_memory=max_memory,
                                        test_case_dir=test_case_dir,
                                        submission_dir=submission_dir,
                                        output=output)

                run_result = judge_client.run()

                # print(run_result)

                # 如果has_test，则返回结果，否则写入数据库中
                if has_test:
                    return run_result
                else:
                    # 将结果写入数据库即可
                    # 这里需要db对象
                    print('执行插入数据库')
                    if bool(judge_state_id):
                        # 需要返回：result,cpu_time,memory,length,id
                        result = result_a = result_b = cpu_time = memory = length = 0
                        for index, item in enumerate(run_result):
                            cpu_time = max(cpu_time, item['cpu_time'])
                            memory = max(memory, item['memory'])
                            result_a = min(result_a, item['result'])
                            result_b = max(result_b, item['result'])
                        result = result_b if result_b > 0 else result_a
                        sql = "UPDATE t_new_judge_status t SET result={result},cpu_time={cpu_time},memory={memory} WHERE t.id = {id}"
                        sql = sql.format(result=result, cpu_time=cpu_time, memory=memory, id=judge_state_id)
                        print(sql)
                        with engine.connect() as connection:
                            connection.execute(sql)
                    pass

if __name__ == "__main__":
    data = {'lang':'cpp', 
    'src': r"""
    #include <iostream>

    using namespace std;

    int main() {
        cout << "ok" << endl;
        return 0;
    }
    """,
    'max_cpu_time': 1000,
    'max_memory': 1024*1024*64,
    'test_case': [{'input':'1 2'}],
    'output': True, judge_state_id: 1}
    _handle(data)